This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see http://rmarkdown.rstudio.com.

When you click the Knit button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:

Intro

In this workshop, you will learn how to read quantitative databases (or data sets) in R. These databases have a typical structure composed of rows (or observations), columns (or variables), and values (also called data points). We also say that these values or data points are located in cells, the intersection between a particular row and variable.

Structure of Database
Structure of Database

In particular, we focus on databases storage in Excel.

Why Excel? Because many scholars, institutions, and organizations storage their data in Excel files. Many people also learn Excel before R, and migrate for the former to the later when learning statistics and data science. It is also not uncommon that our quantitative skills get better while we still struggle to reproduce what we tended to do in Excel in R.

From this viewpoint, this workshop is a first step to close the gap between your Excel skills and your R skills in managing data (i.e, data wrangling and pre-processing).

We will focus on three different types of Excel files: xls., xlsx., and csv. And we will cover multiple common scenarios when working with those files.

Packages

We will work mainly with the readxl library, created by Hadley Wickham. He is one of the Data Scientist behind tidyverse, a famous collection of R package.

Installing

We’ll install several packages at once.

# Set the CRAN mirror
options(repos = "https://cran.r-project.org")
install.packages(c("readxl", "writexl", "purrr"))
## 
## The downloaded binary packages are in
##  /var/folders/ll/khc5jd09519gycf27wrhyzt80000gn/T//Rtmp6pbcnX/downloaded_packages
?cli

Loading

We’ll load several packages at one applying lappy.

packages_to_load <- c("readxl", "writexl", "cli", "purrr", "stringr", "stringi") 
lapply(packages_to_load, library, character.only=TRUE)
## [[1]]
## [1] "readxl"    "stats"     "graphics"  "grDevices" "utils"     "datasets" 
## [7] "methods"   "base"     
## 
## [[2]]
## [1] "writexl"   "readxl"    "stats"     "graphics"  "grDevices" "utils"    
## [7] "datasets"  "methods"   "base"     
## 
## [[3]]
##  [1] "cli"       "writexl"   "readxl"    "stats"     "graphics"  "grDevices"
##  [7] "utils"     "datasets"  "methods"   "base"     
## 
## [[4]]
##  [1] "purrr"     "cli"       "writexl"   "readxl"    "stats"     "graphics" 
##  [7] "grDevices" "utils"     "datasets"  "methods"   "base"     
## 
## [[5]]
##  [1] "stringr"   "purrr"     "cli"       "writexl"   "readxl"    "stats"    
##  [7] "graphics"  "grDevices" "utils"     "datasets"  "methods"   "base"     
## 
## [[6]]
##  [1] "stringi"   "stringr"   "purrr"     "cli"       "writexl"   "readxl"   
##  [7] "stats"     "graphics"  "grDevices" "utils"     "datasets"  "methods"  
## [13] "base"

Importing Excel Files

A single data frame

We will use a set of data frames included in the readxl package. They are all together in a single Excel file but separated into different sheets.

We use the readxl_example function to call the specific data set we want and storage it in the object named xlsx_example.

xlsx_example <- readxl_example("datasets.xlsx")

We can now check the name of the different sheets.

excel_sheets(xlsx_example)
## [1] "iris"     "mtcars"   "chickwts" "quakes"

So, the first sheet contains the iris data, while the last one contains the quake data. In total, there are four sheets containing the following data sets:

  • iris: “This famous (Fisher’s or Anderson’s) iris data set gives the measurements in centimeters of the variables…for 50 flowers from each of 3 species of iris.”
  • mtcars: “The data was extracted from the 1974 Motor Trend US magazine, and comprises fuel consumption and 10 aspects of automobile design and performance for 32 automobiles (1973–74 models).”
  • chickwts: “An experiment was conducted to measure and compare the effectiveness of various feed supplements on the growth rate of chickens.
  • quakes: “The data set give the locations of 1000 seismic events of MB > 4.0. The events occurred in a cube near Fiji since 1964.”

Now, use the read_excel function to open the Excel file.

read_excel(xlsx_example)

Notice that it opens the first sheet (iris) by default – the function is using the argument sheet=1. You can use either the sheet number or its name.

Let’s run the code again but making explicit sheet=1. Also, save this code in the iris object to keep the data in our Global Environment.

iris <- read_excel(xlsx_example, sheet=1) #You do not need sheet=1

We can rapidly explore our data set.

iris

First, we can check the structure of the data.

str(iris)
## tibble [150 × 5] (S3: tbl_df/tbl/data.frame)
##  $ Sepal.Length: num [1:150] 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num [1:150] 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num [1:150] 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num [1:150] 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : chr [1:150] "setosa" "setosa" "setosa" "setosa" ...

We can also check the variables names.

names(iris)
## [1] "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width"  "Species"

Additionally, we can explore the first values. In this case, we are asking the first 10 values.

head(iris , n=10)

However, the important point here is that we can call a specific sheet in an Excel file.

Exercise 1

Read and save the chickwts data set from the “datasets.xlsx”. Hint: you can use excel_sheets again to check the sheets located in the data set.

Multiple sheets

What happens when we want to read multiple sheets at the same time? We can do so by using purrr functions. The purrr package enhances functional programming. Check the following:

“…if you’ve never heard of FP before, the best place to start is the family of map() functions which allow you to replace many for loops with code that is both more succinct and easier to read.” (Purr 1.0.2 Reference)

Indeed, we will apply the map() function with the purpose of using a programming tool or strategy called iteration. Iterations helps when you want to apply the same function to multiple objects, but you want to avoid duplicated code and reduce time (i.e., doing it efficiently). It means you can replace several lines of duplicated code for a single one and perform the same function across multiple objects. Recall: R objects include vectors, data sets, and lists.

Question 1

Does iteration sounds similar to something you do in Excel? Hint: think about dragging a formula.

Now, the map function requires two arguments. First, we need to specify inputs; that is, a list of elements. The second part of map is simple the function we are applying to the list. For example, check the following image:

! Caption for the image

The list are the databases separated into sheets in the “datasets.xlsx”. To access them, we use again excel_sheets.

sheet_names <-excel_sheets(xlsx_example)
sheet_names
## [1] "iris"     "mtcars"   "chickwts" "quakes"

But we add a nuance here, which is the set_names function that will assign names of the elements to the output map will create. By default, the names are the original ones in “datasets.xlsx”. We can also assign different names to the sheets.

set_names(sheet_names, c("data_1", "data_2", "data_3", "data_4"))
##     data_1     data_2     data_3     data_4 
##     "iris"   "mtcars" "chickwts"   "quakes"

So, the previous line of code with two functions – set_names and sheet_names – provides our inputs. We can save it.

input_sheets <- set_names(sheet_names, c("data_1", "data_2", "data_3", "data_4"))

Meanwhile, the function we will apply to each element within input_sheets is read_excel. We use map(). Notice that path is the route to accessing the database. We will use this argument (path) later on.

list_dataframes <-map(input_sheets, #input
   read_excel, path = xlsx_example) #function and path

So, list_dataframes is an object containing our four dataframes, named “data_1”, “data_2”, “data_3”, and “data_4”.

list_dataframes
## $data_1
## # A tibble: 150 × 5
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
##           <dbl>       <dbl>        <dbl>       <dbl> <chr>  
##  1          5.1         3.5          1.4         0.2 setosa 
##  2          4.9         3            1.4         0.2 setosa 
##  3          4.7         3.2          1.3         0.2 setosa 
##  4          4.6         3.1          1.5         0.2 setosa 
##  5          5           3.6          1.4         0.2 setosa 
##  6          5.4         3.9          1.7         0.4 setosa 
##  7          4.6         3.4          1.4         0.3 setosa 
##  8          5           3.4          1.5         0.2 setosa 
##  9          4.4         2.9          1.4         0.2 setosa 
## 10          4.9         3.1          1.5         0.1 setosa 
## # ℹ 140 more rows
## 
## $data_2
## # A tibble: 32 × 11
##      mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
##    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
##  1  21       6  160    110  3.9   2.62  16.5     0     1     4     4
##  2  21       6  160    110  3.9   2.88  17.0     0     1     4     4
##  3  22.8     4  108     93  3.85  2.32  18.6     1     1     4     1
##  4  21.4     6  258    110  3.08  3.22  19.4     1     0     3     1
##  5  18.7     8  360    175  3.15  3.44  17.0     0     0     3     2
##  6  18.1     6  225    105  2.76  3.46  20.2     1     0     3     1
##  7  14.3     8  360    245  3.21  3.57  15.8     0     0     3     4
##  8  24.4     4  147.    62  3.69  3.19  20       1     0     4     2
##  9  22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2
## 10  19.2     6  168.   123  3.92  3.44  18.3     1     0     4     4
## # ℹ 22 more rows
## 
## $data_3
## # A tibble: 71 × 2
##    weight feed     
##     <dbl> <chr>    
##  1    179 horsebean
##  2    160 horsebean
##  3    136 horsebean
##  4    227 horsebean
##  5    217 horsebean
##  6    168 horsebean
##  7    108 horsebean
##  8    124 horsebean
##  9    143 horsebean
## 10    140 horsebean
## # ℹ 61 more rows
## 
## $data_4
## # A tibble: 1,000 × 5
##      lat  long depth   mag stations
##    <dbl> <dbl> <dbl> <dbl>    <dbl>
##  1 -20.4  182.   562   4.8       41
##  2 -20.6  181.   650   4.2       15
##  3 -26    184.    42   5.4       43
##  4 -18.0  182.   626   4.1       19
##  5 -20.4  182.   649   4         11
##  6 -19.7  184.   195   4         12
##  7 -11.7  166.    82   4.8       43
##  8 -28.1  182.   194   4.4       15
##  9 -28.7  182.   211   4.7       35
## 10 -17.5  180.   622   4.3       19
## # ℹ 990 more rows

Now, we will want to separate the databases and save them separately in the Global Environment. The base R list2env function is very useful for these purposes.

list2env(list_dataframes, envir = .GlobalEnv) #save in Global Environment
## <environment: R_GlobalEnv>

Done! We can now work with the four different data frames.

Exercise 2

Let’s put your new skills into work. The “sample.xlsx” data setis located in the following link. We want you to perform three tasks:

Common Scenarios

Let´s keep using “sample.xlsx”. Imagine you only want to keep the first 20 rows in bank-full. You can work with data_a in our Global Environment. Alternatively, you can do it directly while reading the data using n_max.

read_excel(path = "/Users/danielencinas/Downloads/sample.xlsx", 
           sheet = 1,
           n_max = 20)

For removing 10000 rows, we can use the skip argument. The problem is that we also remove the column names. So, we will use the col_names argument.

First, we store the column names from data_a. Let us repeat the three steps above.

getwd()
## [1] "/Users/danielencinas"
setwd("/Users/danielencinas/Downloads")
sheet_names2 <- excel_sheets(path="sample.xlsx")
sheet_names2
## [1] "bank-full" "iris"
input_sheets2 <- set_names(sheet_names2, c("data_a", "data_b"))
sheet_names2
## [1] "bank-full" "iris"
map(input_sheets2, read_excel, path = "/Users/danielencinas/Downloads/sample.xlsx") %>%
  list2env(envir = .GlobalEnv)
## <environment: R_GlobalEnv>

Now, we can work with data_a.

columns_a <- colnames(data_a)
columns_a
##  [1] "age"       "job"       "marital"   "education" "default"   "balance"  
##  [7] "housing"   "loan"      "contact"   "day"       "month"     "duration" 
## [13] "campaign"  "pdays"     "previous"  "poutcome"  "y"

Then, we specify the column names in the col_names argument.

read_excel(path = "/Users/danielencinas/Downloads/sample.xlsx", 
           sheet = 1,
           skip = 10000,
           col_names =columns_a)

For specifying the column type, we use the col_type argument. But notice that we can effectively remove variables when setting “skip”.

read_excel(
  path = "/Users/danielencinas/Downloads/sample.xlsx",
  sheet = 1,
  skip = 10000,
  col_types = c(
    "text",
    "text",
    "text",
    "text",
    "text",
    "numeric",
    "skip",
    "skip",
    "skip",
    "skip",
    "skip",
    "skip",
    "skip",
    "skip",
    "skip",
    "skip",
    "skip"
  )
)

For instance, the previous code only keeps 6 out of 17 variables.

Now, you can also select only a range of rows, columns, or cells (i.e., the intersection between rows and columns). First, let us start by selecting rows.

read_excel(path = "/Users/danielencinas/Downloads/sample.xlsx", 
           sheet = 1,
           range = cell_rows(1:10000))

Similarly, we can select only the first six rows.

read_excel(path = "/Users/danielencinas/Downloads/sample.xlsx", 
           sheet = 1,
           range = cell_cols(1:6))

Finally, we can select cells. Recall Excel name rows as letters and columns as numbers.

read_excel(path = "/Users/danielencinas/Downloads/sample.xlsx", 
           sheet = 1,
           range = "A1:F45211")

The read_excel function supports both xlsx. and xls. files. What about csv. files (i.e., comma delimited files)? We use the read_csv function or read.csv. But let’s first write some csv. files.

Writing Excel Files

A single data frame

First, we can save data_1 as iris.

write.csv(data_1, file = "iris.csv")

Alternatively, you can save the data as a xlsx. document.

write_xlsx(data_1, "iris.xlsx")

Exercise 3

Save data_2 using its original name. Also, save it as xls. rather than xlsx. Hint: notice above the way to specify the file type.

Multiple data frames at once

We can also write multiple data frames. First, let us save a list of data frames.

list_dataframes2 <-c("data_1", "data_2", "data_3", "data_4")

We can now write a function to rename and save csv. files in our computer

write_csv_list <- function(df_list) { #The function starts here
  for (i in seq_along(df_list)) { #A for loop starts here
    
    name <- df_list[i] #We can extract the names of the data

    path<-paste0("/Users/danielencinas/Downloads/new_", name ,".csv") #Change accordingly
    
    write.csv(df_list[[i]], file=path) #Export as csv and save using path above
    
  } #Here it ends the loop
  
} #The end of the function

Let us understand the function bit by bit. First, we extract the names of data bases name with indexing (df_list[i]) . For instance, you can find the second name. In the function above, we save the output as name.

list_dataframes2[2]
## [1] "data_2"

We also specify the path argument in our function. We use paste0 to create a pattern with the specific path as well as name I am using. For instance, it works as follows with data_1

paste0("/Users/danielencinas/Downloads/new_", "data_1",".csv") 
## [1] "/Users/danielencinas/Downloads/new_data_1.csv"

Finally, apply the function to our list.

write_csv_list(list_dataframes2)

Exercise 4

Sources:


  1. Northwestern University↩︎

LS0tCnRpdGxlOiAiSW1wb3J0aW5nIGFuZCBFeHBvcnRpbmcgRXhjZWwgZmlsZXMiCmF1dGhvcjogIkRhbmllbCBFbmNpbmFzIFpldmFsbG9zIF5bTm9ydGh3ZXN0ZXJuIFVuaXZlcnNpdHldIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogVFJVRQogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDIKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KClRoaXMgaXMgYW4gUiBNYXJrZG93biBkb2N1bWVudC4gTWFya2Rvd24gaXMgYSBzaW1wbGUgZm9ybWF0dGluZyBzeW50YXggZm9yIGF1dGhvcmluZyBIVE1MLCBQREYsIGFuZCBNUyBXb3JkIGRvY3VtZW50cy4gRm9yIG1vcmUgZGV0YWlscyBvbiB1c2luZyBSIE1hcmtkb3duIHNlZSA8aHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbT4uCgpXaGVuIHlvdSBjbGljayB0aGUgKipLbml0KiogYnV0dG9uIGEgZG9jdW1lbnQgd2lsbCBiZSBnZW5lcmF0ZWQgdGhhdCBpbmNsdWRlcyBib3RoIGNvbnRlbnQgYXMgd2VsbCBhcyB0aGUgb3V0cHV0IG9mIGFueSBlbWJlZGRlZCBSIGNvZGUgY2h1bmtzIHdpdGhpbiB0aGUgZG9jdW1lbnQuIFlvdSBjYW4gZW1iZWQgYW4gUiBjb2RlIGNodW5rIGxpa2UgdGhpczoKCiMgSW50cm8KCkluIHRoaXMgd29ya3Nob3AsIHlvdSB3aWxsIGxlYXJuIGhvdyB0byByZWFkICpxdWFudGl0YXRpdmUqIGRhdGFiYXNlcyAob3IgZGF0YSBzZXRzKSBpbiBSLiBUaGVzZSBkYXRhYmFzZXMgaGF2ZSBhIHR5cGljYWwgc3RydWN0dXJlIGNvbXBvc2VkIG9mIHJvd3MgKG9yIG9ic2VydmF0aW9ucyksIGNvbHVtbnMgKG9yIHZhcmlhYmxlcyksIGFuZCB2YWx1ZXMgKGFsc28gY2FsbGVkIGRhdGEgcG9pbnRzKS4gV2UgYWxzbyBzYXkgdGhhdCB0aGVzZSB2YWx1ZXMgb3IgZGF0YSBwb2ludHMgYXJlIGxvY2F0ZWQgaW4gY2VsbHMsIHRoZSBpbnRlcnNlY3Rpb24gYmV0d2VlbiBhIHBhcnRpY3VsYXIgcm93IGFuZCB2YXJpYWJsZS4KCiFbU3RydWN0dXJlIG9mIERhdGFiYXNlXSgvVXNlcnMvZGFuaWVsZW5jaW5hcy9Eb3dubG9hZHMvc3RydWN0dXJlLnBuZykKCkluIHBhcnRpY3VsYXIsIHdlIGZvY3VzIG9uIGRhdGFiYXNlcyBzdG9yYWdlIGluIEV4Y2VsLgoKV2h5IEV4Y2VsPyBCZWNhdXNlIG1hbnkgc2Nob2xhcnMsIGluc3RpdHV0aW9ucywgYW5kIG9yZ2FuaXphdGlvbnMgc3RvcmFnZSB0aGVpciBkYXRhIGluIEV4Y2VsIGZpbGVzLiBNYW55IHBlb3BsZSBhbHNvIGxlYXJuIEV4Y2VsIGJlZm9yZSBSLCBhbmQgbWlncmF0ZSBmb3IgdGhlIGZvcm1lciB0byB0aGUgbGF0ZXIgd2hlbiBsZWFybmluZyBzdGF0aXN0aWNzIGFuZCBkYXRhIHNjaWVuY2UuIEl0IGlzIGFsc28gbm90IHVuY29tbW9uIHRoYXQgb3VyIHF1YW50aXRhdGl2ZSBza2lsbHMgZ2V0IGJldHRlciB3aGlsZSB3ZSBzdGlsbCBzdHJ1Z2dsZSB0byByZXByb2R1Y2Ugd2hhdCB3ZSB0ZW5kZWQgdG8gZG8gaW4gRXhjZWwgaW4gUi4KCkZyb20gdGhpcyB2aWV3cG9pbnQsIHRoaXMgd29ya3Nob3AgaXMgYSBmaXJzdCBzdGVwIHRvIGNsb3NlIHRoZSBnYXAgYmV0d2VlbiB5b3VyIEV4Y2VsIHNraWxscyBhbmQgeW91ciBSIHNraWxscyBpbiBtYW5hZ2luZyBkYXRhIChpLmUsIGRhdGEgd3JhbmdsaW5nIGFuZCBwcmUtcHJvY2Vzc2luZykuCgpXZSB3aWxsIGZvY3VzIG9uIHRocmVlIGRpZmZlcmVudCB0eXBlcyBvZiBFeGNlbCBmaWxlczogeGxzLiwgeGxzeC4sIGFuZCBjc3YuIEFuZCB3ZSB3aWxsIGNvdmVyIG11bHRpcGxlIGNvbW1vbiBzY2VuYXJpb3Mgd2hlbiB3b3JraW5nIHdpdGggdGhvc2UgZmlsZXMuCgojIFBhY2thZ2VzCgpXZSB3aWxsIHdvcmsgbWFpbmx5IHdpdGggdGhlIGByZWFkeGxgIGxpYnJhcnksIGNyZWF0ZWQgYnkgSGFkbGV5IFdpY2toYW0uIEhlIGlzIG9uZSBvZiB0aGUgRGF0YSBTY2llbnRpc3QgYmVoaW5kICp0aWR5dmVyc2UqLCBhIGZhbW91cyBjb2xsZWN0aW9uIG9mIFIgcGFja2FnZS4KCiMjIEluc3RhbGxpbmcKCldlJ2xsIGluc3RhbGwgc2V2ZXJhbCBwYWNrYWdlcyBhdCBvbmNlLgoKYGBge3J9CiMgU2V0IHRoZSBDUkFOIG1pcnJvcgpvcHRpb25zKHJlcG9zID0gImh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnIikKaW5zdGFsbC5wYWNrYWdlcyhjKCJyZWFkeGwiLCAid3JpdGV4bCIsICJwdXJyciIpKQo/Y2xpCmBgYAoKIyBMb2FkaW5nCgpXZSdsbCBsb2FkIHNldmVyYWwgcGFja2FnZXMgYXQgb25lIGFwcGx5aW5nIGxhcHB5LgoKYGBge3J9CnBhY2thZ2VzX3RvX2xvYWQgPC0gYygicmVhZHhsIiwgIndyaXRleGwiLCAiY2xpIiwgInB1cnJyIiwgInN0cmluZ3IiLCAic3RyaW5naSIpIApsYXBwbHkocGFja2FnZXNfdG9fbG9hZCwgbGlicmFyeSwgY2hhcmFjdGVyLm9ubHk9VFJVRSkKYGBgCgojIEltcG9ydGluZyBFeGNlbCBGaWxlcwoKIyMgQSBzaW5nbGUgZGF0YSBmcmFtZQoKV2Ugd2lsbCB1c2UgYSBzZXQgb2YgZGF0YSBmcmFtZXMgaW5jbHVkZWQgaW4gdGhlIGByZWFkeGxgIHBhY2thZ2UuIFRoZXkgYXJlIGFsbCB0b2dldGhlciBpbiBhIHNpbmdsZSBFeGNlbCBmaWxlIGJ1dCBzZXBhcmF0ZWQgaW50byBkaWZmZXJlbnQgc2hlZXRzLgoKV2UgdXNlIHRoZSBgcmVhZHhsX2V4YW1wbGVgIGZ1bmN0aW9uIHRvIGNhbGwgdGhlIHNwZWNpZmljIGRhdGEgc2V0IHdlIHdhbnQgYW5kIHN0b3JhZ2UgaXQgaW4gdGhlIG9iamVjdCBuYW1lZCBgeGxzeF9leGFtcGxlYC4KCmBgYHtyfQp4bHN4X2V4YW1wbGUgPC0gcmVhZHhsX2V4YW1wbGUoImRhdGFzZXRzLnhsc3giKQpgYGAKCldlIGNhbiBub3cgY2hlY2sgdGhlIG5hbWUgb2YgdGhlIGRpZmZlcmVudCBzaGVldHMuCgpgYGB7cn0KZXhjZWxfc2hlZXRzKHhsc3hfZXhhbXBsZSkKYGBgCgpTbywgdGhlIGZpcnN0IHNoZWV0IGNvbnRhaW5zIHRoZSBpcmlzIGRhdGEsIHdoaWxlIHRoZSBsYXN0IG9uZSBjb250YWlucyB0aGUgcXVha2UgZGF0YS4gSW4gdG90YWwsIHRoZXJlIGFyZSBmb3VyIHNoZWV0cyBjb250YWluaW5nIHRoZSBmb2xsb3dpbmcgZGF0YSBzZXRzOgoKLSAqaXJpczoqICJUaGlzIGZhbW91cyAoRmlzaGVyJ3Mgb3IgQW5kZXJzb24ncykgaXJpcyBkYXRhIHNldCBnaXZlcyB0aGUgbWVhc3VyZW1lbnRzIGluIGNlbnRpbWV0ZXJzIG9mIHRoZSB2YXJpYWJsZXMuLi5mb3IgNTAgZmxvd2VycyBmcm9tIGVhY2ggb2YgMyBzcGVjaWVzIG9mIGlyaXMuIgotICptdGNhcnM6KiAiVGhlIGRhdGEgd2FzIGV4dHJhY3RlZCBmcm9tIHRoZSAxOTc0IE1vdG9yIFRyZW5kIFVTIG1hZ2F6aW5lLCBhbmQgY29tcHJpc2VzIGZ1ZWwgY29uc3VtcHRpb24gYW5kIDEwIGFzcGVjdHMgb2YgYXV0b21vYmlsZSBkZXNpZ24gYW5kIHBlcmZvcm1hbmNlIGZvciAzMiBhdXRvbW9iaWxlcyAoMTk3M+KAkzc0IG1vZGVscykuIgotICpjaGlja3d0czoqICJBbiBleHBlcmltZW50IHdhcyBjb25kdWN0ZWQgdG8gbWVhc3VyZSBhbmQgY29tcGFyZSB0aGUgZWZmZWN0aXZlbmVzcyBvZiB2YXJpb3VzIGZlZWQgc3VwcGxlbWVudHMgb24gdGhlIGdyb3d0aCByYXRlIG9mIGNoaWNrZW5zLgotICpxdWFrZXM6KiAiVGhlIGRhdGEgc2V0IGdpdmUgdGhlIGxvY2F0aW9ucyBvZiAxMDAwIHNlaXNtaWMgZXZlbnRzIG9mIE1CID4gNC4wLiBUaGUgZXZlbnRzIG9jY3VycmVkIGluIGEgY3ViZSBuZWFyIEZpamkgc2luY2UgMTk2NC4iCgpOb3csIHVzZSB0aGUgYHJlYWRfZXhjZWxgIGZ1bmN0aW9uIHRvIG9wZW4gdGhlIEV4Y2VsIGZpbGUuCgpgYGB7cn0KcmVhZF9leGNlbCh4bHN4X2V4YW1wbGUpCmBgYAoKTm90aWNlIHRoYXQgaXQgb3BlbnMgdGhlIGZpcnN0IHNoZWV0IChpcmlzKSBieSBkZWZhdWx0IC0tIHRoZSBmdW5jdGlvbiBpcyB1c2luZyB0aGUgYXJndW1lbnQgYHNoZWV0PTFgLiBZb3UgY2FuIHVzZSBlaXRoZXIgdGhlIHNoZWV0IG51bWJlciBvciBpdHMgbmFtZS4KCkxldCdzIHJ1biB0aGUgY29kZSBhZ2FpbiBidXQgbWFraW5nIGV4cGxpY2l0IGBzaGVldD0xYC4gQWxzbywgc2F2ZSB0aGlzIGNvZGUgaW4gdGhlIGlyaXMgb2JqZWN0IHRvIGtlZXAgdGhlIGRhdGEgaW4gb3VyIEdsb2JhbCBFbnZpcm9ubWVudC4KCmBgYHtyfQppcmlzIDwtIHJlYWRfZXhjZWwoeGxzeF9leGFtcGxlLCBzaGVldD0xKSAjWW91IGRvIG5vdCBuZWVkIHNoZWV0PTEKYGBgCgpXZSBjYW4gcmFwaWRseSBleHBsb3JlIG91ciBkYXRhIHNldC4gCgpgYGB7cn0KaXJpcwpgYGAKCkZpcnN0LCB3ZSBjYW4gY2hlY2sgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YS4KCmBgYHtyfQpzdHIoaXJpcykKYGBgCgpXZSBjYW4gYWxzbyBjaGVjayB0aGUgdmFyaWFibGVzIG5hbWVzLgoKYGBge3J9Cm5hbWVzKGlyaXMpCmBgYAoKQWRkaXRpb25hbGx5LCB3ZSBjYW4gZXhwbG9yZSB0aGUgZmlyc3QgdmFsdWVzLiBJbiB0aGlzIGNhc2UsIHdlIGFyZSBhc2tpbmcgdGhlIGZpcnN0IDEwIHZhbHVlcy4KCmBgYHtyfQpoZWFkKGlyaXMgLCBuPTEwKQpgYGAKCkhvd2V2ZXIsIHRoZSBpbXBvcnRhbnQgcG9pbnQgaGVyZSBpcyB0aGF0IHdlIGNhbiBjYWxsIGEgc3BlY2lmaWMgc2hlZXQgaW4gYW4gRXhjZWwgZmlsZS4gCgojIEV4ZXJjaXNlIDEKClJlYWQgYW5kIHNhdmUgdGhlIGNoaWNrd3RzIGRhdGEgc2V0IGZyb20gdGhlICJkYXRhc2V0cy54bHN4Ii4gSGludDogeW91IGNhbiB1c2UgYGV4Y2VsX3NoZWV0c2AgYWdhaW4gdG8gY2hlY2sgdGhlIHNoZWV0cyBsb2NhdGVkIGluIHRoZSBkYXRhIHNldC4KCmBgYHtyfQoKYGBgCgojIyBNdWx0aXBsZSBzaGVldHMKCldoYXQgaGFwcGVucyB3aGVuIHdlIHdhbnQgdG8gcmVhZCBtdWx0aXBsZSBzaGVldHMgYXQgdGhlIHNhbWUgdGltZT8gV2UgY2FuIGRvIHNvIGJ5IHVzaW5nIGBwdXJycmAgZnVuY3Rpb25zLiBUaGUgYHB1cnJyYCBwYWNrYWdlIGVuaGFuY2VzIGZ1bmN0aW9uYWwgcHJvZ3JhbW1pbmcuIENoZWNrIHRoZSBmb2xsb3dpbmc6Cgo+ICIuLi5pZiB5b3XigJl2ZSBuZXZlciBoZWFyZCBvZiBGUCBiZWZvcmUsIHRoZSBiZXN0IHBsYWNlIHRvIHN0YXJ0IGlzIHRoZSBmYW1pbHkgb2YgbWFwKCkgZnVuY3Rpb25zIHdoaWNoIGFsbG93IHlvdSB0byByZXBsYWNlIG1hbnkgZm9yIGxvb3BzIHdpdGggY29kZSB0aGF0IGlzIGJvdGggbW9yZSBzdWNjaW5jdCBhbmQgZWFzaWVyIHRvIHJlYWQuIiAoW1B1cnIgMS4wLjIgUmVmZXJlbmNlXShodHRwczovL3B1cnJyLnRpZHl2ZXJzZS5vcmcvKSkKCkluZGVlZCwgd2Ugd2lsbCBhcHBseSB0aGUgYG1hcCgpYCBmdW5jdGlvbiB3aXRoIHRoZSBwdXJwb3NlIG9mIHVzaW5nIGEgcHJvZ3JhbW1pbmcgdG9vbCBvciBzdHJhdGVneSBjYWxsZWQgKml0ZXJhdGlvbiouIEl0ZXJhdGlvbnMgaGVscHMgd2hlbiB5b3Ugd2FudCB0byBhcHBseSB0aGUgc2FtZSBmdW5jdGlvbiB0byBtdWx0aXBsZSBvYmplY3RzLCBidXQgeW91IHdhbnQgdG8gYXZvaWQgZHVwbGljYXRlZCBjb2RlIGFuZCByZWR1Y2UgdGltZSAoaS5lLiwgZG9pbmcgaXQgZWZmaWNpZW50bHkpLiBJdCBtZWFucyB5b3UgY2FuIHJlcGxhY2Ugc2V2ZXJhbCBsaW5lcyBvZiBkdXBsaWNhdGVkIGNvZGUgZm9yIGEgc2luZ2xlIG9uZSBhbmQgcGVyZm9ybSB0aGUgc2FtZSBmdW5jdGlvbiBhY3Jvc3MgbXVsdGlwbGUgb2JqZWN0cy4gUmVjYWxsOiBSIG9iamVjdHMgaW5jbHVkZSB2ZWN0b3JzLCBkYXRhIHNldHMsIGFuZCBsaXN0cy4gIAoKIyBRdWVzdGlvbiAxCgpEb2VzIGl0ZXJhdGlvbiBzb3VuZHMgc2ltaWxhciB0byBzb21ldGhpbmcgeW91IGRvIGluIEV4Y2VsPyBIaW50OiB0aGluayBhYm91dCBkcmFnZ2luZyBhIGZvcm11bGEuIAoKTm93LCB0aGUgYG1hcGAgZnVuY3Rpb24gcmVxdWlyZXMgdHdvIGFyZ3VtZW50cy4gRmlyc3QsIHdlIG5lZWQgdG8gc3BlY2lmeSBpbnB1dHM7IHRoYXQgaXMsIGEgbGlzdCBvZiBlbGVtZW50cy4gVGhlIHNlY29uZCBwYXJ0IG9mIGBtYXBgIGlzIHNpbXBsZSB0aGUgZnVuY3Rpb24gd2UgYXJlIGFwcGx5aW5nIHRvIHRoZSBsaXN0LiBGb3IgZXhhbXBsZSwgY2hlY2sgdGhlIGZvbGxvd2luZyBpbWFnZToKCiEgW0NhcHRpb24gZm9yIHRoZSBpbWFnZV0oL1VzZXJzL2RhbmllbGVuY2luYXMvRG93bmxvYWRzL21hcC5wbmcpCgpUaGUgbGlzdCBhcmUgdGhlIGRhdGFiYXNlcyBzZXBhcmF0ZWQgaW50byBzaGVldHMgaW4gdGhlICJkYXRhc2V0cy54bHN4Ii4gVG8gYWNjZXNzIHRoZW0sIHdlIHVzZSBhZ2FpbiBgZXhjZWxfc2hlZXRzYC4gIAoKYGBge3J9CnNoZWV0X25hbWVzIDwtZXhjZWxfc2hlZXRzKHhsc3hfZXhhbXBsZSkKc2hlZXRfbmFtZXMKYGBgCgpCdXQgd2UgYWRkIGEgbnVhbmNlIGhlcmUsIHdoaWNoIGlzIHRoZSBgc2V0X25hbWVzYCBmdW5jdGlvbiB0aGF0IHdpbGwgYXNzaWduIG5hbWVzIG9mIHRoZSBlbGVtZW50cyB0byB0aGUgb3V0cHV0IG1hcCB3aWxsIGNyZWF0ZS4gQnkgZGVmYXVsdCwgdGhlIG5hbWVzIGFyZSB0aGUgb3JpZ2luYWwgb25lcyBpbiAiZGF0YXNldHMueGxzeCIuIFdlIGNhbiBhbHNvIGFzc2lnbiBkaWZmZXJlbnQgbmFtZXMgdG8gdGhlIHNoZWV0cy4gCgpgYGB7cn0Kc2V0X25hbWVzKHNoZWV0X25hbWVzLCBjKCJkYXRhXzEiLCAiZGF0YV8yIiwgImRhdGFfMyIsICJkYXRhXzQiKSkKYGBgCgoKU28sIHRoZSBwcmV2aW91cyBsaW5lIG9mIGNvZGUgd2l0aCB0d28gZnVuY3Rpb25zIC0tIHNldF9uYW1lcyBhbmQgc2hlZXRfbmFtZXMgLS0gcHJvdmlkZXMgb3VyIGlucHV0cy4gV2UgY2FuIHNhdmUgaXQuCgpgYGB7cn0KaW5wdXRfc2hlZXRzIDwtIHNldF9uYW1lcyhzaGVldF9uYW1lcywgYygiZGF0YV8xIiwgImRhdGFfMiIsICJkYXRhXzMiLCAiZGF0YV80IikpCmBgYAoKTWVhbndoaWxlLCB0aGUgZnVuY3Rpb24gd2Ugd2lsbCBhcHBseSB0byBlYWNoIGVsZW1lbnQgd2l0aGluIGlucHV0X3NoZWV0cyBpcyBgcmVhZF9leGNlbGAuIFdlIHVzZSBgbWFwKClgLiBOb3RpY2UgdGhhdCBgcGF0aGAgaXMgdGhlIHJvdXRlIHRvIGFjY2Vzc2luZyB0aGUgZGF0YWJhc2UuIFdlIHdpbGwgdXNlIHRoaXMgYXJndW1lbnQgKGBwYXRoYCkgbGF0ZXIgb24uIAoKYGBge3J9Cmxpc3RfZGF0YWZyYW1lcyA8LW1hcChpbnB1dF9zaGVldHMsICNpbnB1dAogICByZWFkX2V4Y2VsLCBwYXRoID0geGxzeF9leGFtcGxlKSAjZnVuY3Rpb24gYW5kIHBhdGgKYGBgCgpTbywgbGlzdF9kYXRhZnJhbWVzIGlzIGFuIG9iamVjdCBjb250YWluaW5nIG91ciBmb3VyIGRhdGFmcmFtZXMsIG5hbWVkICJkYXRhXzEiLCAiZGF0YV8yIiwgImRhdGFfMyIsIGFuZCAiZGF0YV80Ii4KCmBgYHtyfQpsaXN0X2RhdGFmcmFtZXMKYGBgCgpOb3csIHdlIHdpbGwgd2FudCB0byBzZXBhcmF0ZSB0aGUgZGF0YWJhc2VzIGFuZCBzYXZlIHRoZW0gc2VwYXJhdGVseSBpbiB0aGUgR2xvYmFsIEVudmlyb25tZW50LiBUaGUgYmFzZSBSIGBsaXN0MmVudmAgZnVuY3Rpb24gaXMgdmVyeSB1c2VmdWwgZm9yIHRoZXNlIHB1cnBvc2VzLgoKYGBge3J9Cmxpc3QyZW52KGxpc3RfZGF0YWZyYW1lcywgZW52aXIgPSAuR2xvYmFsRW52KSAjc2F2ZSBpbiBHbG9iYWwgRW52aXJvbm1lbnQKYGBgCgpEb25lISBXZSBjYW4gbm93IHdvcmsgd2l0aCB0aGUgZm91ciBkaWZmZXJlbnQgZGF0YSBmcmFtZXMuIAoKIyBFeGVyY2lzZSAyCgpMZXQncyBwdXQgeW91ciBuZXcgc2tpbGxzIGludG8gd29yay4gVGhlICAic2FtcGxlLnhsc3giIGRhdGEgc2V0aXMgbG9jYXRlZCBpbiB0aGUgZm9sbG93aW5nIFtsaW5rXShodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xcFdBNE5HdFRmcUtfcFFHMmI4QmRySXVVck1Kc0dIYkUvZWRpdCNnaWQ9MTgwNTg1MTE4NykuIFdlIHdhbnQgeW91IHRvIHBlcmZvcm0gdGhyZWUgdGFza3M6CgotIEZpcnN0LCBjaGVjayB0aGUgc2hlZXQgbmFtZXMgYW5kIHNhdmUgdGhlbSBpbnRvIHRoZSAqc2hlZXRfbmFtZXMyKi4gSGludDogdGhlcmUgYXJlIHR3byBzaGVldHMuCgpgYGB7cn0KCmBgYAoKLSBTZWNvbmQsIGNoYW5nZSB0aGUgc2hlZXQgbmFtZXMgdG8gKmRhdGFfYSogYW5kICpkYXRhX2IqLiBTYXZlIHRoZW0gaW50byB0aGUgKmlucHV0X3NoZWV0czIqIG9iamVjdC4KCmBgYHtyfQoKYGBgCgotIEZpbmFsbHksIHJlYWQgYW5kIHNhdmUgZWFjaCBkYXRhIHNldCBpbnRvIHRoZSBHbG9iYWwgRW52aXJvbm1lbnQuIEhpbnQ6IHVzZSBgcGF0aGAgdG8gc3BlY2lmeSB0aGUgbG9jYXRpb24gb2YgInNhbXBsZS54bHN4IiBpbiB5b3VyIGNvbXB1dGVyLiAKCmBgYHtyfQoKYGBgCgojIyBDb21tb24gU2NlbmFyaW9zCgpMZXTCtHMga2VlcCB1c2luZyAic2FtcGxlLnhsc3giLiBJbWFnaW5lIHlvdSBvbmx5IHdhbnQgdG8ga2VlcCB0aGUgZmlyc3QgMjAgcm93cyBpbiAqYmFuay1mdWxsKi4gWW91IGNhbiB3b3JrIHdpdGggKmRhdGFfYSogaW4gb3VyIEdsb2JhbCBFbnZpcm9ubWVudC4gQWx0ZXJuYXRpdmVseSwgeW91IGNhbiBkbyBpdCBkaXJlY3RseSB3aGlsZSByZWFkaW5nIHRoZSBkYXRhIHVzaW5nIGBuX21heGAuCgpgYGB7cn0KcmVhZF9leGNlbChwYXRoID0gIi9Vc2Vycy9kYW5pZWxlbmNpbmFzL0Rvd25sb2Fkcy9zYW1wbGUueGxzeCIsIAogICAgICAgICAgIHNoZWV0ID0gMSwKICAgICAgICAgICBuX21heCA9IDIwKQpgYGAKCkZvciByZW1vdmluZyAxMDAwMCByb3dzLCB3ZSBjYW4gdXNlIHRoZSBgc2tpcGAgYXJndW1lbnQuIFRoZSBwcm9ibGVtIGlzIHRoYXQgd2UgYWxzbyByZW1vdmUgdGhlIGNvbHVtbiBuYW1lcy4gU28sIHdlIHdpbGwgdXNlIHRoZSBgY29sX25hbWVzYCBhcmd1bWVudC4KCkZpcnN0LCB3ZSBzdG9yZSB0aGUgY29sdW1uIG5hbWVzIGZyb20gKmRhdGFfYSouIExldCB1cyByZXBlYXQgdGhlIHRocmVlIHN0ZXBzIGFib3ZlLgoKYGBge3J9CmdldHdkKCkKc2V0d2QoIi9Vc2Vycy9kYW5pZWxlbmNpbmFzL0Rvd25sb2FkcyIpCnNoZWV0X25hbWVzMiA8LSBleGNlbF9zaGVldHMocGF0aD0ic2FtcGxlLnhsc3giKQpzaGVldF9uYW1lczIKaW5wdXRfc2hlZXRzMiA8LSBzZXRfbmFtZXMoc2hlZXRfbmFtZXMyLCBjKCJkYXRhX2EiLCAiZGF0YV9iIikpCnNoZWV0X25hbWVzMgpgYGAKCmBgYHtyfQptYXAoaW5wdXRfc2hlZXRzMiwgcmVhZF9leGNlbCwgcGF0aCA9ICIvVXNlcnMvZGFuaWVsZW5jaW5hcy9Eb3dubG9hZHMvc2FtcGxlLnhsc3giKSAlPiUKICBsaXN0MmVudihlbnZpciA9IC5HbG9iYWxFbnYpCmBgYAoKTm93LCB3ZSBjYW4gd29yayB3aXRoICpkYXRhX2EuKgoKYGBge3J9CmNvbHVtbnNfYSA8LSBjb2xuYW1lcyhkYXRhX2EpCmNvbHVtbnNfYQpgYGAKClRoZW4sIHdlIHNwZWNpZnkgdGhlIGNvbHVtbiBuYW1lcyBpbiB0aGUgYGNvbF9uYW1lc2AgYXJndW1lbnQuCgpgYGB7cn0KcmVhZF9leGNlbChwYXRoID0gIi9Vc2Vycy9kYW5pZWxlbmNpbmFzL0Rvd25sb2Fkcy9zYW1wbGUueGxzeCIsIAogICAgICAgICAgIHNoZWV0ID0gMSwKICAgICAgICAgICBza2lwID0gMTAwMDAsCiAgICAgICAgICAgY29sX25hbWVzID1jb2x1bW5zX2EpCmBgYAoKRm9yIHNwZWNpZnlpbmcgdGhlIGNvbHVtbiB0eXBlLCB3ZSB1c2UgdGhlIGBjb2xfdHlwZWAgYXJndW1lbnQuIEJ1dCBub3RpY2UgdGhhdCB3ZSBjYW4gZWZmZWN0aXZlbHkgcmVtb3ZlIHZhcmlhYmxlcyB3aGVuIHNldHRpbmcgInNraXAiLgoKYGBge3J9CnJlYWRfZXhjZWwoCiAgcGF0aCA9ICIvVXNlcnMvZGFuaWVsZW5jaW5hcy9Eb3dubG9hZHMvc2FtcGxlLnhsc3giLAogIHNoZWV0ID0gMSwKICBza2lwID0gMTAwMDAsCiAgY29sX3R5cGVzID0gYygKICAgICJ0ZXh0IiwKICAgICJ0ZXh0IiwKICAgICJ0ZXh0IiwKICAgICJ0ZXh0IiwKICAgICJ0ZXh0IiwKICAgICJudW1lcmljIiwKICAgICJza2lwIiwKICAgICJza2lwIiwKICAgICJza2lwIiwKICAgICJza2lwIiwKICAgICJza2lwIiwKICAgICJza2lwIiwKICAgICJza2lwIiwKICAgICJza2lwIiwKICAgICJza2lwIiwKICAgICJza2lwIiwKICAgICJza2lwIgogICkKKQoKYGBgCgpGb3IgaW5zdGFuY2UsIHRoZSBwcmV2aW91cyBjb2RlIG9ubHkga2VlcHMgNiBvdXQgb2YgMTcgdmFyaWFibGVzLiAKCk5vdywgeW91IGNhbiBhbHNvIHNlbGVjdCBvbmx5IGEgcmFuZ2Ugb2Ygcm93cywgY29sdW1ucywgb3IgY2VsbHMgKGkuZS4sIHRoZSBpbnRlcnNlY3Rpb24gYmV0d2VlbiByb3dzIGFuZCBjb2x1bW5zKS4gRmlyc3QsIGxldCB1cyBzdGFydCBieSBzZWxlY3Rpbmcgcm93cy4KCmBgYHtyfQpyZWFkX2V4Y2VsKHBhdGggPSAiL1VzZXJzL2RhbmllbGVuY2luYXMvRG93bmxvYWRzL3NhbXBsZS54bHN4IiwgCiAgICAgICAgICAgc2hlZXQgPSAxLAogICAgICAgICAgIHJhbmdlID0gY2VsbF9yb3dzKDE6MTAwMDApKQpgYGAKClNpbWlsYXJseSwgd2UgY2FuIHNlbGVjdCBvbmx5IHRoZSBmaXJzdCBzaXggcm93cy4KCmBgYHtyfQpyZWFkX2V4Y2VsKHBhdGggPSAiL1VzZXJzL2RhbmllbGVuY2luYXMvRG93bmxvYWRzL3NhbXBsZS54bHN4IiwgCiAgICAgICAgICAgc2hlZXQgPSAxLAogICAgICAgICAgIHJhbmdlID0gY2VsbF9jb2xzKDE6NikpCmBgYAoKRmluYWxseSwgd2UgY2FuIHNlbGVjdCBjZWxscy4gUmVjYWxsIEV4Y2VsIG5hbWUgcm93cyBhcyBsZXR0ZXJzIGFuZCBjb2x1bW5zIGFzIG51bWJlcnMuIAoKYGBge3J9CnJlYWRfZXhjZWwocGF0aCA9ICIvVXNlcnMvZGFuaWVsZW5jaW5hcy9Eb3dubG9hZHMvc2FtcGxlLnhsc3giLCAKICAgICAgICAgICBzaGVldCA9IDEsCiAgICAgICAgICAgcmFuZ2UgPSAiQTE6RjQ1MjExIikKYGBgCgpUaGUgYHJlYWRfZXhjZWxgIGZ1bmN0aW9uIHN1cHBvcnRzIGJvdGggeGxzeC4gYW5kIHhscy4gZmlsZXMuIFdoYXQgYWJvdXQgY3N2LiBmaWxlcyAoaS5lLiwgY29tbWEgZGVsaW1pdGVkIGZpbGVzKT8gV2UgdXNlIHRoZSBgcmVhZF9jc3ZgIGZ1bmN0aW9uIG9yIGByZWFkLmNzdmAuIEJ1dCBsZXQncyBmaXJzdCB3cml0ZSBzb21lIGNzdi4gZmlsZXMuIAoKIyBXcml0aW5nIEV4Y2VsIEZpbGVzCgojIyBBIHNpbmdsZSBkYXRhIGZyYW1lCgpGaXJzdCwgd2UgY2FuIHNhdmUgZGF0YV8xIGFzIGlyaXMuIAoKYGBge3J9CndyaXRlLmNzdihkYXRhXzEsIGZpbGUgPSAiaXJpcy5jc3YiKQpgYGAKCkFsdGVybmF0aXZlbHksIHlvdSBjYW4gc2F2ZSB0aGUgZGF0YSBhcyBhIHhsc3guIGRvY3VtZW50LgoKYGBge3J9CndyaXRlX3hsc3goZGF0YV8xLCAiaXJpcy54bHN4IikKYGBgCgojIEV4ZXJjaXNlIDMKClNhdmUgZGF0YV8yIHVzaW5nIGl0cyBvcmlnaW5hbCBuYW1lLiBBbHNvLCBzYXZlIGl0IGFzIHhscy4gcmF0aGVyIHRoYW4geGxzeC4gIEhpbnQ6IG5vdGljZSBhYm92ZSB0aGUgd2F5IHRvIHNwZWNpZnkgdGhlIGZpbGUgdHlwZS4gCgojIyBNdWx0aXBsZSBkYXRhIGZyYW1lcyBhdCBvbmNlCgpXZSBjYW4gYWxzbyB3cml0ZSBtdWx0aXBsZSBkYXRhIGZyYW1lcy4gRmlyc3QsIGxldCB1cyBzYXZlIGEgbGlzdCBvZiBkYXRhIGZyYW1lcy4gCgpgYGB7cn0KbGlzdF9kYXRhZnJhbWVzMiA8LWMoImRhdGFfMSIsICJkYXRhXzIiLCAiZGF0YV8zIiwgImRhdGFfNCIpCmBgYAogIApXZSBjYW4gbm93IHdyaXRlIGEgZnVuY3Rpb24gdG8gcmVuYW1lIGFuZCBzYXZlIGNzdi4gZmlsZXMgaW4gb3VyIGNvbXB1dGVyCgpgYGB7cn0Kd3JpdGVfY3N2X2xpc3QgPC0gZnVuY3Rpb24oZGZfbGlzdCkgeyAjVGhlIGZ1bmN0aW9uIHN0YXJ0cyBoZXJlCiAgZm9yIChpIGluIHNlcV9hbG9uZyhkZl9saXN0KSkgeyAjQSBmb3IgbG9vcCBzdGFydHMgaGVyZQogICAgCiAgICBuYW1lIDwtIGRmX2xpc3RbaV0gI1dlIGNhbiBleHRyYWN0IHRoZSBuYW1lcyBvZiB0aGUgZGF0YQoKICAgIHBhdGg8LXBhc3RlMCgiL1VzZXJzL2RhbmllbGVuY2luYXMvRG93bmxvYWRzL25ld18iLCBuYW1lICwiLmNzdiIpICNDaGFuZ2UgYWNjb3JkaW5nbHkKICAgIAogICAgd3JpdGUuY3N2KGRmX2xpc3RbW2ldXSwgZmlsZT1wYXRoKSAjRXhwb3J0IGFzIGNzdiBhbmQgc2F2ZSB1c2luZyBwYXRoIGFib3ZlCiAgICAKICB9ICNIZXJlIGl0IGVuZHMgdGhlIGxvb3AKICAKfSAjVGhlIGVuZCBvZiB0aGUgZnVuY3Rpb24KYGBgCgpMZXQgdXMgdW5kZXJzdGFuZCB0aGUgZnVuY3Rpb24gYml0IGJ5IGJpdC4gRmlyc3QsIHdlIGV4dHJhY3QgdGhlIG5hbWVzIG9mIGRhdGEgYmFzZXMgbmFtZSB3aXRoIGluZGV4aW5nIChkZl9saXN0W2ldKSAuIEZvciBpbnN0YW5jZSwgeW91IGNhbiBmaW5kIHRoZSBzZWNvbmQgbmFtZS4gSW4gdGhlIGZ1bmN0aW9uIGFib3ZlLCB3ZSBzYXZlIHRoZSBvdXRwdXQgYXMgbmFtZS4KCmBgYHtyfQpsaXN0X2RhdGFmcmFtZXMyWzJdCmBgYAoKV2UgYWxzbyBzcGVjaWZ5IHRoZSBgcGF0aGAgYXJndW1lbnQgaW4gb3VyIGZ1bmN0aW9uLiBXZSB1c2UgcGFzdGUwIHRvIGNyZWF0ZSBhIHBhdHRlcm4gd2l0aCB0aGUgc3BlY2lmaWMgcGF0aCBhcyB3ZWxsIGFzIG5hbWUgSSBhbSB1c2luZy4gRm9yIGluc3RhbmNlLCBpdCB3b3JrcyBhcyBmb2xsb3dzIHdpdGggKmRhdGFfMSoKCmBgYHtyfQpwYXN0ZTAoIi9Vc2Vycy9kYW5pZWxlbmNpbmFzL0Rvd25sb2Fkcy9uZXdfIiwgImRhdGFfMSIsIi5jc3YiKSAKYGBgCgpGaW5hbGx5LCBhcHBseSB0aGUgZnVuY3Rpb24gdG8gb3VyIGxpc3QuCgpgYGB7cn0Kd3JpdGVfY3N2X2xpc3QobGlzdF9kYXRhZnJhbWVzMikKYGBgCgojIEV4ZXJjaXNlIDQKCi0gU2F2ZSAqZGF0YV9hKiBhbmQgKmRhdGFfYiogYXMgY3N2LgotIFJlYWQgdGhlIGNzdi4gZmlsZQoKU291cmNlczoKCi0gW0RhdGEgQ2FtcF0oaHR0cHM6Ly93d3cuZGF0YWNhbXAuY29tL3R1dG9yaWFsL3ItdHV0b3JpYWwtcmVhZC1leGNlbC1pbnRvLXIpCi0gICBbUi1ibG9nZ2Vyc10oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vMjAyMi8wNy9yZWFkLWRhdGEtZnJvbS1tdWx0aXBsZS1leGNlbC1zaGVldHMtYW5kLWNvbnZlcnQtdGhlbS10by1pbmRpdmlkdWFsLWRhdGEtZnJhbWVzLykKLSAgIFtTdGFja292ZXJmbG93XShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy81MDY5NTg2MC91c2luZy13cml0ZS14bHN4LWluc2lkZS1sYXBwbHktdG8td3JpdGUtZWFjaC1kYXRhLWZyYW1lLWluLWdsb2JhbC1lbnZpcm9ubWVudC10bykKLSAgIFtSLWJsb2dnZXJzXShodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS8yMDIyLzA0L2luZGV4aW5nLWl0ZXJhdGlvbnMtd2l0aC1zZXRfbmFtZXMvKQo=